package com.sunlee.admin.api.module.job.template;

import cn.hutool.core.util.ZipUtil;
import com.aspose.slides.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sunlee.admin.api.constant.StringConstant;
import com.sunlee.admin.api.module.job.dto.GeneratePptTaskDto;
import com.sunlee.admin.api.module.job.template.context.FilePath;
import com.sunlee.admin.api.module.job.template.context.GeneratePptContext;
import com.sunlee.admin.api.util.FileUtil;
import com.sunlee.common.core.utils.StringUtil;
import com.sunlee.common.core.constant.Constant;
import com.sunlee.common.core.exception.BusinessException;
import com.sunlee.common.core.utils.DateUtils;
import com.sunlee.common.service.entity.ThirdProductEntity;
import com.sunlee.common.service.enums.ThirdResStatusEnum;
import com.sunlee.common.service.service.ThirdProductService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.List;
import java.util.*;

/**
 * PPT解析模板，一般只需处理removeSlide、handleText即可
 *
 * @author sunlee
 * @date 2023-03-04
 */
@Slf4j
@Component
public abstract class GeneratePptTemplate {

    @Autowired
    private ThirdProductService thirdProductService;

    static {
        InputStream license = Thread.currentThread().getContextClassLoader().getResourceAsStream("license.xml");
        if (license != null) {
            License aposeLic = new License();
            aposeLic.setLicense(license);
        }
    }

    public final void process(GeneratePptContext context) {

        // 流程初始化
        init(context);

        if (!context.isSkipProcess()) {
            listProcess(context);
        }

        // 流程处理结束
        end(context);
    }

    protected void end(GeneratePptContext context) {
    }

    private void listProcess(GeneratePptContext context) {
        // 遍历待解析构造的实体集合
        for (ThirdProductEntity entity : context.getThirdProductEntityList()) {
            log.info("=====开始处理PPT====={},{}", entity.getThirdResId(), entity.getTitle());
            context.setCurThirdProductEntity(entity);
            initLoop(context);
            doProcess(context);
            endLoop(context);
        }
    }

    protected void endLoop(GeneratePptContext context) {
        update(context);
    }

    protected void init(GeneratePptContext context) {
        log.info("数据初始化开始");
        GeneratePptTaskDto dto = context.getDto();

        // 查询所有需要解析的PPT文件
        List<ThirdProductEntity> fileInfoList = thirdProductService.list(new LambdaQueryWrapper<ThirdProductEntity>()
                // 指定第三方文件来源
                .eq(ThirdProductEntity::getSource, dto.getSource())
                // 指定第三方资源ID
                .in(CollectionUtils.isNotEmpty(dto.getThirdResId()), ThirdProductEntity::getThirdResId, dto.getThirdResId())
                // 只处理状态为有源PPT资源的，如果指定第三方资源ID则不限制
                .eq(CollectionUtils.isEmpty(dto.getThirdResId()), ThirdProductEntity::getStatus, ThirdResStatusEnum.OBTAIN_PPT.getCode())
                // 限制数量，如果指定第三方资源ID或者未设置则不限制
                .last(CollectionUtils.isEmpty(dto.getThirdResId()) && Objects.nonNull(dto.getTotal()), "ORDER BY RAND() LIMIT " + dto.getTotal()));

        if (CollectionUtils.isEmpty(fileInfoList)) {
            context.setSkipProcess(true);
            return;
        }

        // 1、设置资源存储目录（路径默认加年月日）
        dto.setTargetFolder(FileUtil.appendSeparator(dto.getTargetFolder()) + FileUtil.appendSeparator(DateUtils.nowDateForYmd()));

        // 2、设置需遍历的实体列表
        context.setThirdProductEntityList(fileInfoList);

        // 3、初始化文路径件对象
        context.setFilePath(new FilePath());

        // 4、初始化系统字体
        context.setLocalFontNames(Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()));

        // 5、初始化基本母版
        initBaseMasterSlide(context);
    }

    protected void initLoop(GeneratePptContext context) {
        // 初始化文件路径
        handleFilePath(context);
    }

    private void handleFilePath(GeneratePptContext context) {
        ThirdProductEntity entity = context.getCurThirdProductEntity();

        // 如果存在旧文件，先删除
        String generateDir = entity.getGenerateDir();
        if (StringUtils.isNotBlank(generateDir)) {
            log.info("移除旧文件");
            cn.hutool.core.io.FileUtil.del(generateDir);
        }

        String targetFolder = context.getDto().getTargetFolder();

        String title = entity.getTitle();

        // ppt文件路径（ppt文件存储路径 = 用户指定目录 + 年月 + 日 + 第三方资源ID_资源标题）
        targetFolder = targetFolder + FileUtil.appendSeparator(entity.getThirdResId() + Constant.SYMBOL_UNDERLINE + title);

        // 设置进当前实体
        entity.setGenerateDir(targetFolder);

        if (!FileUtil.makeDirectory(new File(targetFolder))) {
            throw new BusinessException("创建文件夹失败:" + targetFolder);
        }

        String fileTargetFolder = targetFolder + title;

        FilePath filePath = context.getFilePath();
        filePath.setHomeImg(fileTargetFolder + StringConstant.FIRST_JPG);
        filePath.setMultiImg(fileTargetFolder + StringConstant.NINE_JPG);
        filePath.setAllImg(fileTargetFolder + StringConstant.ALL_JPG);
        filePath.setTargetFilePath(fileTargetFolder + StringConstant.PPTX);
        filePath.setTargetFilePathZip(fileTargetFolder + StringConstant.ZIP);
    }

    public static void main(String[] args) throws Exception {
        String path = "D:\\小鸡PPT\\old-ppt\\2023-03\\06\\98848_深蓝色“科技改变世界”.pptx";
        FileInputStream fileInputStream = new FileInputStream(path);

        Presentation pres = new Presentation(fileInputStream);
        BufferedImage image = pres.getSlides().get_Item(0).getThumbnail(0.75f, 0.75f);
        File outImage = new File("D:\\小鸡PPT\\old-ppt\\2023-03\\06\\98848_深蓝色“科技改变世界”.jpg");
        ImageIO.write(image, StringConstant.JPG, outImage);
    }

    private void doProcess(GeneratePptContext context) {
        ThirdProductEntity entity = context.getCurThirdProductEntity();
        FilePath filePath = context.getFilePath();
        try {
            if (!FileUtil.isFileExist(entity.getPptPath())) {
                throw new BusinessException("原始PPT文件不存在");
            }

            try (FileInputStream fileInputStream = new FileInputStream(entity.getPptPath())) {
                // 1、获取文件
                Presentation pres = new Presentation(fileInputStream);
                context.setPresentation(pres);

                // 2、移除不需要幻灯片
                removeSlide(context);

                // 3、特殊处理幻灯片
                if (isHandleSlide(context)) {
                    handleSlide(context);
                }

                // 3、处理文件元数据
                handleProperties(context);

                // 4、记录缺失字体
                handleMissingFont(context);

                // 5、替换文本
                handleText(context);

                // 6、生成图片：首图、全图、多图
                handleImage(context);

                // 7、生成修改后的PPT
                try (FileOutputStream out = new FileOutputStream(filePath.getTargetFilePath())) {
                    pres.save(out, SaveFormat.Pptx);
                }

                // 8、将PPT添加到压缩包
                handlePackage(context);
            }
        } catch (Exception e) {
            log.error("PPT处理异常：{}", context.getCurThirdProductEntity().getThirdResId(), e);
            entity.setStatus(ThirdResStatusEnum.ERROR.getCode());
            entity.setStatusMsg(StringUtil.subString(e.getMessage(), 0, 2000));
            return;
        }
        entity.setStatus(ThirdResStatusEnum.RESOLVED.getCode());
    }

    protected void update(GeneratePptContext context) {
        if (!thirdProductService.updateById(context.getCurThirdProductEntity())) {
            throw new BusinessException("更新记录失败");
        }
    }

    protected void handlePackage(GeneratePptContext context) {
        FilePath filePath = context.getFilePath();
        ZipUtil.zip(filePath.getTargetFilePath(), filePath.getTargetFilePathZip());
    }

    /**
     * 是否走特殊处理
     *
     * @param context 上下文
     * @return true走流程，false不走流程
     */
    protected abstract boolean isHandleSlide(GeneratePptContext context);

    /**
     * 特殊处理幻灯片：增加logo、名称等
     *
     * @param context 上下文
     */
    protected void handleSlide(GeneratePptContext context) {

    }

    protected void handleImage(GeneratePptContext context) throws BusinessException {
        try {
            createHomeImg(context);
            createAllImg(context);
            createImgByNum(context);
        } catch (Exception e) {
            log.error("图片生成异常", e);
            throw new BusinessException("幻灯片图片生成失败");
        }
    }

    /**
     * 根据需要移除幻灯片
     *
     * @param context 上下文
     */
    protected abstract void removeSlide(GeneratePptContext context);

    protected void handleMissingFont(GeneratePptContext context) {

        Set<String> missFontSet = new LinkedHashSet<>();

        // 查询系统字体
        List<String> localFontNames = context.getLocalFontNames();

        IFontData[] fonts = context.getPresentation().getFontsManager().getFonts();

        // 获取全局缺失字体，最多5个
        int fontNum = 5;
        for (IFontData font : fonts) {
            if (fontNum <= 0) {
                break;
            }
            if (!localFontNames.contains(font.getFontName())) {
                fontNum--;
                missFontSet.add(font.getFontName());
            }
        }

        if (CollectionUtils.isNotEmpty(missFontSet)) {
            context.getCurThirdProductEntity().setMissFont(String.join(",", missFontSet));
        }
    }

    protected void createHomeImg(GeneratePptContext context) throws IOException {
        BufferedImage image = context.getPresentation().getSlides().get_Item(0).getThumbnail(0.70f, 0.70f);
        File outImage = new File(context.getFilePath().getHomeImg());
        ImageIO.write(image, StringConstant.JPG, outImage);
    }

    protected void createImgByNum(GeneratePptContext context) throws IOException {
        ISlideCollection slides = context.getPresentation().getSlides();
        Integer multiImgNum = context.getFilePath().getMultiImgNum();

        // 数量图不超过最大幻灯片数
        int size = slides.size();
        if (size < multiImgNum) {
            multiImgNum = size;
        }

        // 保证总数是奇数
        if (multiImgNum % 2 == 0) {
            multiImgNum--;
        }

        int oneW = 1024;
        int oneH = 576;

        int bgW = 1024;
        int bgH = oneH + ((oneH / 2) * (multiImgNum - 1) / 2);


        int twoW = bgW / 2;
        int twoH = oneH / 2;

        BufferedImage bg = new BufferedImage(bgW, bgH, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = bg.createGraphics();

        int changeH = oneH;
        boolean oddNumber;

        // 固定第一张大图
        g2d.drawImage(slides.get_Item(0).getThumbnail(0.70f, 0.70f), 0, 0, oneW, oneH, null);

        for (int i = 1; i < slides.size(); i++) {

            if (i > multiImgNum - 1) {
                break;
            }
            oddNumber = i % 2 != 0;
            g2d.drawImage(slides.get_Item(i).getThumbnail(0.70f, 0.70f),
                    oddNumber ? 0 : twoW,
                    changeH,
                    twoW,
                    twoH,
                    null);

            if (!oddNumber) {
                changeH += twoH;
            }
        }

        File outImage = new File(context.getFilePath().getMultiImg());
        ImageIO.write(bg, StringConstant.JPG, outImage);
    }

    protected void createAllImg(GeneratePptContext context) throws IOException {

        ISlideCollection slides = context.getPresentation().getSlides();
        int size = slides.size() - 1;

        int bgW = 1024;
        int bgH;

        // 是否奇数个幻灯片
        boolean oddNumSize = size % 2 != 0;
        if (oddNumSize) {
            // 奇数个
            bgH = 2 * 576 + ((size / 2) * (576 / 2));
        } else {
            // 偶数个
            bgH = 576 + ((size / 2) * (576 / 2));
        }


        int oneW = 1024;
        int oneH = 576;

        int twoW = bgW / 2;
        int twoH = oneH / 2;

        BufferedImage bg = new BufferedImage(bgW, bgH, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = bg.createGraphics();

        int changeH = oneH;
        boolean oddNumber;

        // 固定第一张大图
        g2d.drawImage(slides.get_Item(0).getThumbnail(0.70f, 0.70f), 0, 0, oneW, oneH, null);

        for (int i = 1; i < slides.size(); i++) {

            oddNumber = i % 2 != 0;

            // 当为奇数张时，最后一张图也是大图
            if (oddNumSize && (i == size)) {
                g2d.drawImage(slides.get_Item(i).getThumbnail(0.70f, 0.70f),
                        0,
                        changeH,
                        oneW,
                        oneH,
                        null);
            } else {
                g2d.drawImage(slides.get_Item(i).getThumbnail(0.70f, 0.70f),
                        oddNumber ? 0 : twoW,
                        changeH,
                        twoW,
                        twoH,
                        null);
            }

            if (!oddNumber) {
                changeH += twoH;
            }
        }

        File outImage = new File(context.getFilePath().getAllImg());
        ImageIO.write(bg, StringConstant.JPG, outImage);
    }

    /**
     * 处理文本
     *
     * @param context 上下文
     */
    protected abstract void handleText(GeneratePptContext context);

    /**
     * 处理文件元数据
     */
    protected void handleProperties(GeneratePptContext context) {

        ThirdProductEntity thirdProductEntity = context.getCurThirdProductEntity();
        IDocumentProperties pros = context.getPresentation().getDocumentProperties();
        Date nowDate = new Date();

        //公司
        pros.setCompany("");
        //管理者
        pros.setManager(StringConstant.APP_NAME_PPT);
        //演示文稿格式
        pros.setPresentationFormat(FileUtil.getTypePart(thirdProductEntity.getPptPath()));
        //模板
        pros.setApplicationTemplate("");
        //标题
        pros.setTitle(thirdProductEntity.getTitle());
        //主题
        pros.setSubject(thirdProductEntity.getTag());
        //作者
        pros.setAuthor("");
        //标记
        pros.setKeywords("");
        //备注
        pros.setComments("");
        //类别
        pros.setCategory("");
        //创建内容时间
        pros.setCreatedTime(nowDate);
        //最后一次打印时间
        pros.setLastPrinted(nowDate);
        //最后一次保存者
        pros.setLastSavedBy("");
        //修订号
        pros.setRevisionNumber(1);
        //内容状态
        pros.setContentStatus("");
    }

    /**
     * 获取初始幻灯片母版：在init方法底部执行，非必须流程
     *
     * @param context 上下文
     */
    protected void initBaseMasterSlide(GeneratePptContext context) {
        try (FileInputStream fileInputStream = new FileInputStream(context.getDto().getBaseMasterPptPath())) {
            Presentation basePresentation = new Presentation(fileInputStream);
            context.setMasterSlide(basePresentation.getMasters().get_Item(0));
        } catch (Exception e) {
            log.info("获取母版PPT失败", e);
            throw new BusinessException("获取母版PPT失败");
        }
    }
}
